﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using RemoteLinqBase;

namespace WCFCollection
{
	/// <summary>
	/// Serialization tools are used to serialize LLBLGen entities and normal objects.
	/// </summary>
	public static class SerializationTool
	{
        public static string ConvertAnonymousNameForSerialization(Type anonymousType)
        {
            return "AnonymousSurrogated";
        }

        /// <summary>
        /// Contains registered types for datacontract serialization
        /// </summary>
        private static Type[] _RegisteredTypes = new Type[0];

        /// <summary>
        /// Gets the registred types.
        /// </summary>
        /// <value>The registred types.</value>
        public static Type[] RegisteredTypes
        {
            get
            {
                return _RegisteredTypes;
            }
        }

        /// <summary>
        /// Adds the new type of the registered.
        /// </summary>
        /// <param name="type">The type.</param>
        public static void AddNewRegisteredType(Type type)
        {
            // add type to registered type array
            Type[] temp = new Type[_RegisteredTypes.Length + 1];
            _RegisteredTypes.CopyTo(temp, 0);
            temp.SetValue(type, _RegisteredTypes.Length);
            _RegisteredTypes = temp;
        }

		/// <summary>
		/// Toes the object.
		/// </summary>
		/// <param name="objectType">Type of the object.</param>
		/// <param name="xml">The XML.</param>
		/// <param name="registeredTypes">The registered types.</param>
		/// <returns>Object instance.</returns>
		public static object ToObject(Type objectType, System.Xml.Linq.XElement xml)
		{
			using (XmlReader reader = xml.CreateReader())
			{
                DataContractSerializer serializer;
                if (objectType.Name.StartsWith("<>") || (objectType.IsGenericType && objectType.GetGenericArguments()[0].Name.StartsWith("<>")))
                {
                    serializer = new DataContractSerializer(objectType, RegisteredTypes,
                        int.MaxValue, false, false, new DataContractSurrogate(objectType));
                }
                else
                {
                    serializer = new DataContractSerializer(objectType, RegisteredTypes);
                }
				return serializer.ReadObject(reader);
			}
		}

		/// <summary>
		/// Collections to XML.
		/// </summary>
		/// <param name="result">The result.</param>
		/// <param name="collection">The collection.</param>
		/// <param name="registeredTypes">The registered types.</param>
		/// <returns>Collection as XML.</returns>
		public static XElement CollectionToXML(object result, string collection)
		{
            
			// if collection contains llblgen items.
			if (result is IEnumerable)
			{
                
				using (MemoryStream ms = new MemoryStream())
				{
					using (XmlTextWriter xml = new XmlTextWriter(ms, Encoding.UTF8))
					{
                        Type objectType = result.GetType().GetGenericArguments()[0];
                        if (objectType.Name.StartsWith("<>"))
                        {
                            collection = ConvertAnonymousNameForSerialization(objectType) + "List";
                            // start main tag
                            xml.WriteStartElement(string.Format("ArrayOf{0}", collection));
                            xml.WriteAttributeString("xmlns:i", "http://www.w3.org/2001/XMLSchema-instance");
                            xml.WriteStartElement("AnonymousData");
                        }
                        else
                        {
                            // start main tag
                            xml.WriteStartElement(string.Format("ArrayOf{0}", collection));
                            xml.WriteAttributeString("xmlns", "http://schemas.datacontract.org/2004/07/" + objectType.Namespace);
                            xml.WriteAttributeString("xmlns:i", "http://www.w3.org/2001/XMLSchema-instance");
                            
                        }
						// serialize entities
						foreach (object item in (result as IEnumerable))
						{
							XElement itemInXML = ToXML(item);
							itemInXML.WriteTo(xml);
							//xml.WriteRaw(itemInXML.ToString());
						}
                        if (result.GetType().IsGenericType && result.GetType().GetGenericArguments()[0].Name.StartsWith("<>"))
                        {
                            xml.WriteEndElement();
                        }
						xml.WriteEndElement();
						xml.Flush();
						ms.Seek(0, SeekOrigin.Begin);
						using (XmlReader reader = XmlReader.Create(ms))
						{
							return System.Xml.Linq.XElement.Load(reader);
						}
					}
				}
			}
			else
			{
				return ToXML(result);
			}
		}

        /// <summary>
        /// Toes the XML.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <returns></returns>
		public static XElement ToXML(object result)
		{
            if (result == null)
            {
                return new XElement("nil");
            }
			using (MemoryStream ms = new MemoryStream())
			{
                DataContractSerializer serializer;
                Type objectType = result.GetType();
                if (objectType.Name.StartsWith("<>") || (objectType.IsGenericType && objectType.GetGenericArguments()[0].Name.StartsWith("<>")))
                {
                    serializer = new DataContractSerializer(objectType, RegisteredTypes,
                        int.MaxValue, false, false, new DataContractSurrogate(objectType));
                }
                else
                {
                    serializer = new DataContractSerializer(objectType, RegisteredTypes);
                }
				serializer.WriteObject(ms, result);
				ms.Seek(0, SeekOrigin.Begin);
				using (XmlReader reader = XmlReader.Create(ms))
				{
					return System.Xml.Linq.XElement.Load(reader);
				}
			}
		}
	}
}
